import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

matplotlib.use('TkAgg')  # 设置后端为TkAgg
# 显示中文，Windows系统
plt.rcParams['font.sans-serif'] = 'SimHei'
# 支持符号
plt.rcParams['axes.unicode_minus'] = False

# 数据集：设置随机种子以保证结果可复现
np.random.seed(42)
# 生成自变量 X: 在0到10之间均匀分布的100个点
X = np.linspace(start=0, stop=10, num=100).reshape(-1, 1)
# 生成因变量 y: 基于线性关系 2*X + 1
y = 2 * X + 1 + np.random.randn(100, 1) * 2

# 将数据集划分为训练集和测试集，测试集占20%
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)


class LinearRegression:
    def __init__(self):
        self.weights = None # 特征权重向量（斜率系数）
        self.bias = None # 偏置项（截距）

    def fit(self, X, y, learning_rate=0.01, epochs=1000):
        """
        训练线性回归模型
        :param X: 特征矩阵，形状为(样本数, 特征数)
        :param y: 目标值向量，形状为(样本数,)
        :param learning_rate:学习率（默认0.01）
        :param epochs:训练迭代次数（默认1000）
        :return:None
        """
        # 权重初始化为零向量，维度匹配特征数
        n_samples, n_features = X.shape
        self.weights = np.zeros((n_features, 1))
        # 偏置初始化为零
        self.bias = 0
        # 梯度下降训练过程
        for _ in range(epochs):
            y_pred = np.dot(X, self.weights) + self.bias

            # 计算梯度
            dw = (1 / n_samples) * np.dot(X.T, (y_pred - y))
            db = (1 / n_samples) * np.sum(y_pred - y)

            # 更新参数
            self.weights -= learning_rate * dw
            self.bias -= learning_rate * db

    def predict(self, X):
        return np.dot(X, self.weights) + self.bias


# 训练模型
model = LinearRegression()
model.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = model.predict(X_test)

# 计算均方误差
mse = np.mean((y_test - y_pred) ** 2)
print(f"均方误差: {mse:.2f}")

# 可视化结果
plt.scatter(X_test, y_test, color='blue', label='实际值')
plt.plot(X_test, y_pred, color='red', linewidth=2, label='预测值')
plt.xlabel('X')
plt.ylabel('y')
plt.title('线性回归预测结果')
plt.legend()
plt.show()
